package com.cah.project.mock.gen;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.EnumUtil;
import com.cah.project.mock.util.GenStrUtil;
import com.cah.project.module.standard.service.IDictTypeService;
import lombok.SneakyThrows;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

/**
 * 功能描述: 生成服务的测试方法 <br/>
 */
public class GenServiceTest {

    /** 项目目录 */
    private static final String PROJECT_MODULE = "project-service";
    /** 引入的包列表 */
    private static final Set<String> IMPORT_LIST = new HashSet<>();

    public static void main(String[] args) throws Exception {
        Class<?> clazz = IDictTypeService.class;
        String className = clazz.getSimpleName();
        String clazzName = GenStrUtil.lowerFirst(className);

        // 包路径
        String packageStr = clazz.getPackage() + ";\n\n";
        // 文件头
        String javaHeadStr = "\n" +
                "/**\n" +
                " * <p> TODO </p>\n" +
                " * \n" +
                " * @author TODO \n" +
                " * @date " + DateUtil.now() + "\n" +
                " */\n" +
                "@SpringBootTest(classes = Application.class)\n" +
                "@RunWith(SpringRunner.class)\n" +
                "public class " + className + "Test {\n";
        // 引入服务
        String autowiredStr = "\n" +
                "    @Autowired\n" +
                "    private " + className + " " + clazzName + ";\n\n";
        // 组装测试方法体（主体逻辑）
        String methodStr = getTestMethods(clazz, clazzName);
        // 添加固定引入包
        IMPORT_LIST.add("import org.junit.Test;\n");
        IMPORT_LIST.add("import org.junit.runner.RunWith;\n");
        IMPORT_LIST.add("import org.springframework.beans.factory.annotation.Autowired;\n");
        IMPORT_LIST.add("import org.springframework.boot.test.context.SpringBootTest;\n");
        IMPORT_LIST.add("import org.springframework.test.context.junit4.SpringRunner;\n");

        // 组装结果
        String str = packageStr +
                GenStrUtil.getClassImport(PROJECT_MODULE, clazz) +
                String.join("", IMPORT_LIST) +
                javaHeadStr +
                autowiredStr +
                methodStr +
                "}\n";
        // 输出
        System.out.println(str);
    }

    /**
     * 功能描述: 组装测试方法体 <br/>
     *
     * @param clazz     类
     * @param clazzName 类的属性名称
     * @return "java.lang.String"
     */
    private static String getTestMethods(Class<?> clazz, String clazzName) {
        StringBuilder sb = new StringBuilder();
        // 获取自身全部的方法
        Method[] methods = clazz.getDeclaredMethods();
        // 方法重载的加序号
        Map<String, Integer> methodNameMap = new HashMap<>();
        for(Method method : methods) {
            if(method.isDefault() || method.getName().contains("$")) {
                // 是 lambda 的，则跳过
                continue;
            }
            // 测试方法名(避免重复)
            String methodName = method.getName();
            int count = Optional.ofNullable(methodNameMap.get(methodName)).orElse(0);
            if(count > 0) {
                methodName = methodName + count;
            }
            methodNameMap.put(methodName, count + 1);
            // 组装方法名
            sb.append("    @Test\n");
            sb.append("    public void test" + GenStrUtil.upperFirst(methodName) + "() {\n");
            // 定义入参名称
            List<String> intoNameList = new ArrayList<>();
            Type[] paramTypes = method.getGenericParameterTypes();
            for(int i = 0; i < paramTypes.length; i++) {
                Type paramType = paramTypes[i];
                // 参数名
                String paramName = "arg" + i;
                // 获取参数类型
                String paramTypeName = GenStrUtil.getTypeSimpleName(paramType);
                // 组装
                sb.append("        " + paramTypeName + " " + paramName + " = " + getNewObject(paramType, paramName));
                // 添加到参数列表中
                intoNameList.add(paramName);
            }
            // 入参集合拼接
            String intoName = CollUtil.isEmpty(intoNameList) ? "" : String.join(", ", intoNameList);
            // 组装执行与返回
            Type returnType = method.getGenericReturnType();
            String execMethod = clazzName + "." + method.getName() + "(" + intoName + ");\n";
            sb.append("        ");
            if(!GenStrUtil.equals("void", returnType.getTypeName())) {
                sb.append(GenStrUtil.getTypeSimpleName(returnType) + " result = ");
            }
            sb.append(execMethod);
            if(!GenStrUtil.equals("void", returnType.getTypeName())) {
                sb.append("        System.out.println(result);\n");
            }
            sb.append("    }\n\n");
        }
        return sb.toString();
    }

    /**
     * 功能描述: 构建新对象 <br/>
     *
     * @param type 对象类型
     * @return "java.lang.String"
     */
    @SneakyThrows
    private static String getNewObject(Type type, String paramName) {
        String typeName = type.getTypeName();
        if (typeName.contains("List")) {
            IMPORT_LIST.add("import java.util.ArrayList;\n");
            return "new ArrayList<>();\n";
        }
        if (typeName.contains("Map")) {
            IMPORT_LIST.add("import java.util.HashMap;\n");
            return "new HashMap<>();\n";
        }
        if(typeName.contains("Integer") || typeName.contains("int")) {
            return "1;\n";
        }
        if(typeName.contains("Long") || typeName.contains("long")) {
            return "1L;\n";
        }
        if(typeName.contains("String")) {
            return "\"\";\n";
        }
        if(typeName.contains("Boolean") || typeName.contains("boolean")) {
            return "false;\n";
        }
        if(typeName.contains("BigDecimal")) {
            return "BigDecimal.ZERO;\n";
        }
        Class<?> clazz = Class.forName(typeName);
        if(EnumUtil.isEnum(clazz)) {
            return GenStrUtil.getTypeSimpleName(type) + "." + EnumUtil.getNames((Class<? extends Enum<?>>) clazz).get(0) + ";\n";
        } else {
            String newStr =  "new " + clazz.getSimpleName() + "();\n";
            // 添加set属性
            StringBuilder sb = new StringBuilder();
            Field[] fields = clazz.getDeclaredFields();
            for(Field field : fields) {
                // 终态和静态不设置
                if(!Modifier.isFinal(field.getModifiers()) && !Modifier.isStatic(field.getModifiers())) {
                    System.out.println();
                    sb.append("        " + paramName + ".set" + GenStrUtil.upperFirst(field.getName()) + "(\"XXXX\");\n");
                }
            }
            return newStr + sb.toString();
        }
    }

}
